Published on

unsafe 与未定义行为(UB

unsafe 与未定义行为(UB)

什么是 UB

UB 是 Undefined Behavior(未定义行为)的缩写。

Rust 编译器假设 safe 代码不会触发 UB。一旦触发,编译器有权做出任何行为——崩溃、静默返回错误数据、甚至看似正常运行但在优化后突然出错,全部都是"合法"的。

常见的 UB 包括:解引用空指针、数据竞争、数组越界访问等。

unsafe 的意义

unsafe 的意义是告诉编译器:这段代码的正确性由程序员担保,编译器不再做安全假设。

当编译器无法验证一个操作是否安全,但程序员可以靠外部知识来保证时,用 unsafe 就是合理的。

实例:ThreadIndex::new

以本项目中 crates/cuda-device/src/thread.rsThreadIndex::new 为例:

pub struct ThreadIndex<'kernel, IndexSpace = Index1D> {
    raw: usize,
    // ...
}

impl<'kernel, IndexSpace> ThreadIndex<'kernel, IndexSpace> {
    unsafe fn new(raw: usize, _scope: &'kernel KernelScope<'kernel>) -> Self {
        ThreadIndex { raw, /* ... */ }
    }
}

ThreadIndex 的设计保证每个线程的索引是唯一的,正是这个唯一性使得对 DisjointSlice 的并行写入无需同步也不会产生数据竞争。如果用户可以随意调用 new() 构造一个假的 ThreadIndex,就能伪造索引、破坏唯一性保证,从而产生数据竞争(UB)。

所以 new() 必须是 unsafe,把"我保证这个索引确实来自硬件线程 ID 计算"的责任推给调用者。模块内部的 index_1dindex_2d 等函数就是这些受信任的调用者——它们通过读取 CUDA 特殊寄存器来计算索引,可以安全地承担这个义务。

什么情况该用 unsafe

简单区分

  • 函数内部能通过运行时检查兜底 → 不需要 unsafe
  • 函数无条件信任调用方传入的值,而该值不合法会导致 UB → 应该用 unsafe

THE END